It's a todo list.
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

at e7e28bd4e96da3a6bfc040425bea28158c4d84e4 173 lines 5.4 kB view raw
1<script lang="ts"> 2 import { onMount, tick } from "svelte"; 3 import { page } from "$app/stores"; 4 import { local_lists, pinned_list, generateId, type List } from "$lib/stores.svelte"; 5 import { goto, pushState } from "$app/navigation"; 6 import toast, { Toaster } from "svelte-french-toast"; 7 8 let is_menu_open = $state(false); 9 let list : List | undefined = $state(); 10 let task_input = $state(""); 11 let user_lists = $derived(local_lists.value) as List[]; 12 13 onMount(() => { 14 list = local_lists.value!.find((l) => l.id === $page.params.id); 15 }); 16 17 // since list points to something inside local_lists, 18 // it will run when list state changes 19 $effect(() => local_lists.update()); 20 21 function addTask() { 22 if (task_input.length === 0) { 23 toast.error("Enter a task to add"); 24 return; 25 } 26 27 list?.tasks.push({ 28 id: generateId(), 29 description: task_input, 30 is_completed: false 31 }); 32 33 task_input = ""; 34 } 35 36 function deleteTask(id: string) { 37 if (list) { 38 list.tasks = list.tasks.filter((t) => t.id !== id); 39 } 40 } 41 42 function createList() { 43 const new_list = { 44 id: generateId(), 45 title: "", 46 tasks: [] 47 }; 48 49 local_lists.value!.push(new_list); 50 list = local_lists.value!.find((l) => l.id === new_list.id); 51 goto(`/${list!.id}`); 52 } 53 54 function switchToList(id: string) { 55 list = local_lists.value!.find((l) => l.id === id); 56 goto(`/${list!.id}`); 57 } 58 59 function pinList(id: string) { 60 pinned_list.value = id; 61 } 62 63 function deleteList() { 64 if (pinned_list.value === $page.params.id) { 65 toast.error("Cannot delete pinned list"); 66 return; 67 } 68 69 local_lists.value = local_lists.value!.filter((l) => l.id !== $page.params.id); 70 list = local_lists.value.find((l) => l.id === pinned_list.value); 71 goto(`/${list!.id}`); 72 } 73</script> 74 75<main class="flex flex-col w-full px-2 pt-8 pb-12 lg:p-4 lg:pb-24 gap-8 text-xl lg:text-3xl"> 76 {#if list} 77 <section class="relative flex gap-4 w-full"> 78 <div class="flex gap-4 border-black border w-fit h-fit p-2 bg-white rounded-xl"> 79 <button onclick={() => is_menu_open = !is_menu_open}> 80 <img 81 src="/list-box-line.svg" 82 alt="Lists button" 83 class="w-12 h-12 hover:bg-slate-500/10 rounded-full" 84 /> 85 </button> 86 <button onclick={() => pinList(list!.id)}> 87 <img 88 src={pinned_list.value === list.id ? "/pin.svg" : "/pin-line.svg"} 89 alt="Pin list button" 90 class="w-12 h-12 hover:bg-slate-500/10 rounded-full" 91 /> 92 </button> 93 <button onclick={deleteList}> 94 <img 95 src="/trash-line.svg" 96 alt="Delete list button" 97 class="w-12 h-12 hover:bg-slate-500/10 rounded-full" 98 /> 99 </button> 100 </div> 101 102 {#if is_menu_open} 103 <menu class="absolute flex flex-col gap-2 w-fit h-fit top-20 p-2 bg-white border border-black rounded-lg !text-black !text-lg"> 104 {#each user_lists as user_list : List (user_list.id)} 105 <button 106 onclick={() => { 107 switchToList(user_list.id) 108 is_menu_open = false; 109 }} 110 class="flex gap-2 justify-between text-start w-full h-full rounded-xl pl-2 pr-5 py-2 hover:bg-slate-500/10 transition-all duration-150 items-center" 111 > 112 {user_list.title.length > 0 ? user_list.title : "Untitled"} 113 {#if user_list.id === list.id} 114 <img src="/check-line.svg" alt="Item 1" class="w-8 h-8" /> 115 {/if} 116 </button> 117 {/each} 118 <button 119 onclick={() => { 120 createList(); 121 is_menu_open = false; 122 }} 123 class="flex gap-2 justify-between text-start w-full h-full rounded-xl pl-2 pr-5 py-2 hover:bg-slate-500/10 transition-all duration-150 items-center" 124 > 125 Create new list 126 </button> 127 </menu> 128 {/if} 129 </section> 130 <input 131 type="text" 132 bind:value={list.title} 133 placeholder="Untitled" 134 class="text-5xl font-bold bg-transparent" 135 /> 136 <ul class="flex flex-col gap-4"> 137 {#each list.tasks as task (task.id)} 138 <li class="group flex justify-between items-center gap-4"> 139 <div class="flex w-full gap-4 items-center pr-4 py-2"> 140 <input 141 type="checkbox" 142 bind:checked={task.is_completed} 143 class="w-6 h-6 bg-transparent" 144 /> 145 <input 146 type="text" 147 bind:value={task.description} 148 class="w-full hover:underline text-ellipsis overflow-hidden bg-transparent" 149 /> 150 </div> 151 152 <div class="flex lg:hidden group-hover:flex gap-4 w-fit"> 153 <button 154 onclick={() => deleteTask(task.id)} 155 class="px-4 py-2 bg-red-500 rounded-xl text-white" 156 > 157 - 158 </button> 159 </div> 160 </li> 161 {/each} 162 <li class="flex gap-4 w-full"> 163 <button onclick={addTask} class="px-5 rounded-full bg-white text-black"> 164 + 165 </button> 166 <input type="text" bind:value={task_input} class="bg-transparent pr-4 py-2 border-b w-full"/> 167 </li> 168 </ul> 169 170 {:else} 171 <p>Loading...</p> 172 {/if} 173</main>